/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.da.dext.genxml;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;

import uk.da.dext.CategoryCollectionType;
import uk.da.dext.CategoryType;
import uk.da.dext.CodeCollectionType;
import uk.da.dext.CodeType;
import uk.da.dext.DocumentType;
import uk.da.dext.DocumentsType;
import uk.da.dext.MemoCollectionType;
import uk.da.dext.MemoDocumentRef;
import uk.da.dext.MemoTextType;
import uk.da.dext.MemoType;
import uk.da.dext.ObjectRelationType;
import uk.da.dext.QudexDocument;
import uk.da.dext.QudexType;
import uk.da.dext.RelationCollectionType;
import uk.da.dext.ResourceCollectionType;
import uk.da.dext.SourceType;
import uk.da.dext.SourcesType;
import uk.da.dext.ObjectRelationType.ObjectType;
import uk.da.dext.ObjectRelationType.RelationName;
import uk.da.dext.SourceType.LocType;
import uk.da.dext.SourceType.ResourceType;
import uk.da.dext.datastructs.CategoryStruct;
import uk.da.dext.datastructs.CodeStruct;
import uk.da.dext.datastructs.DocumentStruct;
import uk.da.dext.datastructs.MemoStruct;
import uk.da.dext.exceptions.QudexException;
import uk.da.dext.utilities.Constants;

public class QudexManagement {
	private Logger log = Logger.getLogger(QudexManagement.class);
	private static final String ID_PREFIX = "Qudex";
	private QudexDocument qudexInstance;
	private ElementsManagement categoryManager;
	private ElementsManagement memoManager;
	private ElementsManagement codeManager;

	public void setQudexInstance(QudexDocument qudexInstance) {
		this.qudexInstance = qudexInstance;
	}

	public QudexDocument getQudexInstance() {
		return qudexInstance;
	}

	/**
	 * @param categoryManager
	 *            the categoryManager to set
	 */
	public void setCategoryManager(ElementsManagement categoryManager) {
		this.categoryManager = categoryManager;
	}

	/**
	 * @return the categoryManager
	 */
	public ElementsManagement getCategoryManager() {
		return categoryManager;
	}

	/**
	 * @param memoManager
	 *            the memoManager to set
	 */
	public void setMemoManager(ElementsManagement memoManager) {
		this.memoManager = memoManager;
	}

	/**
	 * @return the memoManager
	 */
	public ElementsManagement getMemoManager() {
		return memoManager;
	}

	/**
	 * @param codeManager
	 *            the codeManager to set
	 */
	public void setCodeManager(ElementsManagement codeManager) {
		this.codeManager = codeManager;
	}

	/**
	 * @return the codeManager
	 */
	public ElementsManagement getCodeManager() {
		return codeManager;
	}

	public void createQudexInstance() {
		this.qudexInstance = QudexDocument.Factory.newInstance();
	}
	
	public void createQudexInstance(String xmlInstance) throws XmlException {
		this.qudexInstance = QudexDocument.Factory.parse(xmlInstance);
	}
	/**
	 * 
	 * @param locType
	 *            should be 'other'
	 * @param otherLocType
	 *            should be 'fedora-pid'
	 */
	public void createQudexInstance(String locType, String otherLocType) {
		this.qudexInstance = QudexDocument.Factory.newInstance();
		QudexType qType = qudexInstance.addNewQudex();
		// Assign xs:ID
		qType.setId(generateID());
		// Assign creation date
		qType.setCdate(Calendar.getInstance());
		this.addResourceCollectionType();
	}

	/**
	 * Creates the Qudex ResourceCollection element provided the number of
	 * documents in the Qudex collection
	 * 
	 * @param numberOfDocuments
	 *            number of documents associated in the Qudex Instance
	 */
	private void addResourceCollectionType() {
		ResourceCollectionType rcType = this.qudexInstance.getQudex()
				.addNewResourceCollection();
		rcType.setId(generateID());
		SourcesType srcType = rcType.addNewSources();
		srcType.setId(generateID());
		DocumentsType docsType = rcType.addNewDocuments();
		docsType.setId(generateID());
	}

	public boolean generateQudexDocuments(ElementsManagement catManager,
			List<DocumentStruct> documentsList) {
		for (DocumentStruct docS : documentsList) {
			String xmlDocumentId = catManager.searchDocumentIds(docS
					.getFedoraDocumentPid());
			if (xmlDocumentId != null) {
				// Create first source
				String sourceID = generateID();
				docS.setDocumentType(DocumentType.DocumentType2.SOURCE
						.toString());
				docS.setDocumentXmlId(xmlDocumentId);
				docS.setResourceReference(sourceID);
				// TODO Check if the field ResourceReferencePid is needed
				docS.setResourceReferencePid(docS.getFedoraDocumentPid());
				this.addSource(sourceID, docS);
				this.addDocument(docS);
			} else {
				// AMG - a new document ID has to be created
				// Create first source
				String sourceID = generateID();
				docS.setDocumentType(DocumentType.DocumentType2.SOURCE
						.toString());
				docS.setDocumentXmlId(generateID());
				docS.setResourceReference(sourceID);
				docS.setResourceReferencePid(docS.getFedoraDocumentPid());
				this.addSource(sourceID, docS);
				this.addDocument(docS);
			}
		}
		return true;
	}

	/**
	 * Creates a new Source XML element and adds it to the main qudex instance
	 * 
	 * @param xmlDocumentId
	 *            the xmlID associated with this Source
	 */
	private void addSource(String xmlDocumentId, DocumentStruct docS) {
		ResourceCollectionType resCollection = this.qudexInstance.getQudex()
				.getResourceCollection();
		if (resCollection == null) {
			resCollection = this.qudexInstance.getQudex()
					.addNewResourceCollection();
			resCollection.setId(generateID());
			SourcesType sources = resCollection.addNewSources();
			sources.setId(generateID());
		}
		SourceType source = this.qudexInstance.getQudex()
				.getResourceCollection().getSources().addNewSource();
		source.setId(xmlDocumentId);
		ResourceType.Enum enumValue = ResourceType.Enum.forString(docS
				.getResourceType());
		if (enumValue != null)
			source.setResourceType(enumValue);
		else {
			// Other value not coming from the Qudex Controlled Vocabulary
			source.setResourceType(ResourceType.OTHER);
			source.setOtherResourceType(docS.getResourceType());
		}
		// AMG - March Add otherLocType and locationType
		source.setOtherLocType(Constants.QUDEX_SOURCE_LOCATION_TYPE);
		source.setLocType(LocType.OTHER);
		if (docS.getDocumentType() != null
				&& !docS.getDocumentMimeType().equals(""))
			source.setMimeType(docS.getDocumentMimeType());
		if (docS.getDocumentLocation() != null
				&& !docS.getDocumentLocation().equals(""))
			source.setLocation(docS.getDocumentLocation());
		if (docS.getDocumentCreator() != null
				&& !docS.getDocumentCreator().equals(""))
			source.setCreator(docS.getDocumentCreator());
		if (docS.getDocumentLanguage() != null
				&& !docS.getDocumentLanguage().equals(""))
			source.setLanguage(docS.getDocumentLanguage());
	}

	/**
	 * Creates a new Document XML element and adds it to the main qudex instance
	 * 
	 * @param qudexDocument
	 *            document struct containing the document xml fields needed
	 */
	private void addDocument(DocumentStruct qudexDocument) {
		ResourceCollectionType resCollection = this.qudexInstance.getQudex()
				.getResourceCollection();
		if (resCollection == null) {
			resCollection = this.qudexInstance.getQudex()
					.addNewResourceCollection();
			resCollection.setId(generateID());
			DocumentsType documents = resCollection.addNewDocuments();
			documents.setId(generateID());
		}
		DocumentType document = this.qudexInstance.getQudex()
				.getResourceCollection().getDocuments().addNewDocument();
		document.setId(qudexDocument.getDocumentXmlId());
		document.setLabel(qudexDocument.getFedoraDocumentPid());
		DocumentType.DocumentType2.Enum enumValue = DocumentType.DocumentType2.Enum
				.forString(qudexDocument.getDocumentType());
		if (enumValue == null) {
			document.setDocumentType(DocumentType.DocumentType2.SOURCE);
		} else
			document.setDocumentType(enumValue);
		if (qudexDocument.getResourceReference() != null
				&& !qudexDocument.getResourceReference().equals(""))
			document.setResourceRef(qudexDocument.getResourceReference());
		if (qudexDocument.getDocumentCreator() != null
				&& !qudexDocument.getDocumentCreator().equals(""))
			document.setCreator(qudexDocument.getDocumentCreator());
		if (qudexDocument.getDocumentLanguage() != null
				&& !qudexDocument.getDocumentLanguage().equals(""))
			document.setLanguage(qudexDocument.getDocumentLanguage());
	}

	// CATEGORY MANAGEMENT
	/**
	 * @param id
	 * @param documentRefs
	 * @param categoryRefs
	 * @param label
	 * @param displaylabel
	 * 
	 * @return XML String with qudex category snippet, null if not valid XML
	 * @throws QudexException
	 */
	public static String generateCategorySnippet(String id,
			List<String> documentRefs, List<String> categoryRefs, String label,
			String displaylabel, String language) throws QudexException {

		CategoryType catXml = CategoryType.Factory.newInstance();
		if (id == null || id.equals("")) {
			throw new QudexException(
					"Category snippet id null or empty. Invalid Qudex snippet...");
		}
		catXml.setId(id);
		if (label != null && !label.equals(""))
			catXml.setLabel(label);
		if (displaylabel != null && !displaylabel.equals(""))
			catXml.setDisplayLabel(displaylabel);
		if (categoryRefs.size() > 0)
			catXml.setCategoryRefs(categoryRefs);
		if (documentRefs.size() > 0)
			catXml.setDocumentRefs(documentRefs);
		//TODO Categories which have language attribute do not validate correctly
		//if (language != null && !language.equals(""))
			//catXml.setLanguage(language);

		boolean isValidXml = catXml.validate();
		if (isValidXml)
			return catXml.toString();
		else
			return null;
	}

	/**
	 * 
	 * @param categoryXml
	 * @return true if success, false otherwise
	 */
	public boolean addCategory(String categoryXml) {
		CategoryType cat;
		CategoryCollectionType catCollection = this.qudexInstance.getQudex()
				.getCategoryCollection();
		try {
			if (catCollection == null) {
				catCollection = this.qudexInstance.getQudex()
						.addNewCategoryCollection();
				catCollection.setId(generateID());
			}
			CategoryType auxCat = CategoryType.Factory.parse(categoryXml);
			// CHECK IF CATEGORY ALDEADY IN INSTANCE
			if (isCategoryPresent(auxCat))
				return true;
			cat = catCollection.addNewCategory();

			// TODO Check this error checking, empty XMLID?????
			if (!auxCat.getId().equals(""))
				cat.setId(auxCat.getId());
			else
				return false;
			if (auxCat.getCategoryRefs() != null
					&& auxCat.getCategoryRefs().size() > 0)
				cat.setCategoryRefs(auxCat.getCategoryRefs());
			cat.setDisplayLabel(auxCat.getDisplayLabel());
			if (auxCat.getDocumentRefs() != null
					&& auxCat.getDocumentRefs().size() > 0)
				cat.setDocumentRefs(auxCat.getDocumentRefs());
			if (auxCat.getLabel() != null && !auxCat.getLabel().equals(""))
				cat.setLabel(auxCat.getLabel());
			if (auxCat.getLanguage() != null
					&& !auxCat.getLanguage().equals(""))
				cat.setLanguage(auxCat.getLanguage());

			return true;
		} catch (XmlException e) {
			log.fatal(e.getMessage());
			return false;
		}
	}
	
	public boolean updateCategory(String categoryXml) {
		CategoryType cat;
		CategoryCollectionType catCollection = this.qudexInstance.getQudex()
				.getCategoryCollection();
		try {
			if (catCollection == null) {
				catCollection = this.qudexInstance.getQudex()
						.addNewCategoryCollection();
				catCollection.setId(generateID());
			}
			CategoryType auxCat = CategoryType.Factory.parse(categoryXml);
			cat = getCategoryPresent(auxCat.getId());
			
			if (cat != null) {				
				if (auxCat.getCategoryRefs() != null
						&& auxCat.getCategoryRefs().size() > 0) {
					List<String> list = new ArrayList<String>();
					if (cat.getCategoryRefs() != null)
						for (String str : cat.getCategoryRefs())
							list.add(str);
					for (String str : auxCat.getCategoryRefs())
						list.add(str);
					cat.setCategoryRefs(list);
					list.clear();
					for (String str : auxCat.getDocumentRefs())
						list.add(str);
					cat.setDocumentRefs(list);
				}
			}
			return true;
		} catch (Exception e) {
			log.fatal(e.getMessage());
			return false;
		}
	}

	private boolean isCategoryPresent(CategoryType auxCat) {
		for (CategoryType cat : this.qudexInstance.getQudex()
				.getCategoryCollection().getCategoryArray()) {
			if (cat.getId().equals(auxCat.getId()))
				return true;
		}
		return false;
	}
	
	private CategoryType getCategoryPresent(String label) {
		for (CategoryType cat : this.qudexInstance.getQudex()
				.getCategoryCollection().getCategoryArray()) {
			if (cat.getId().equals(label))
				return cat;
		}
		return null;
	}

	/**
	 * @param id
	 * @param label
	 * @param displaylabel
	 * @param authority
	 *            optional
	 * 
	 * @return XML String with qudex code snippet, null if not valid XML
	 * @throws QudexException
	 */
	public static String generateCodeSnippet(String id, String label,
			String displaylabel, String authority, String language)
			throws QudexException {

		CodeType codeXml = CodeType.Factory.newInstance();
		if (id == null || id.equals("")) {
			throw new QudexException(
					"Incorrect format for Qudex Code Snippet: id null or empty...");
		}
		codeXml.setId(id);
		if (label != null && !label.equals(""))
			codeXml.setLabel(label);
		if (displaylabel != null && !displaylabel.equals(""))
			codeXml.setDisplayLabel(displaylabel);
		if (language != null && !language.equals(""))
			codeXml.setLanguage(language);

		if (authority != null && !authority.equals(""))
			codeXml.setAuthority(authority);

		boolean isValidXml = codeXml.validate();
		if (isValidXml)
			return codeXml.toString();
		else
			return null;
	}

	/**
	 * 
	 * @param codeXml
	 * @return true if success, false otherwise
	 */
	public boolean addCode(String codeXml) {
		CodeType code;
		CodeCollectionType codeCollection = this.qudexInstance.getQudex()
				.getCodeCollection();
		try {
			if (codeCollection == null) {
				codeCollection = this.qudexInstance.getQudex()
						.addNewCodeCollection();
				codeCollection.setId(generateID());
			}
			CodeType auxCode = CodeType.Factory.parse(codeXml);
			if (this.isCodePresent(auxCode))
				return true;
			code = codeCollection.addNewCode();
			// Xml ID
			code.setId(auxCode.getId());
			// Authority
			if (auxCode.getAuthority() != null
					&& !auxCode.getAuthority().equals(""))
				code.setAuthority(auxCode.getAuthority());
			// Display Label
			if (auxCode.getDisplayLabel() != null
					&& !auxCode.getDisplayLabel().equals(""))
				code.setDisplayLabel(auxCode.getDisplayLabel());
			// Label
			if (auxCode.getLabel() != null && !auxCode.getLabel().equals(""))
				code.setLabel(auxCode.getLabel());
			// Language
			if (auxCode.getLanguage() != null
					&& !auxCode.getLanguage().equals(""))
				code.setLanguage(auxCode.getLanguage());

			return true;
		} catch (XmlException e) {
			log.fatal(e.getMessage());
			return false;
		}
	}

	private boolean isCodePresent(CodeType auxCode) {
		for (CodeType code : this.qudexInstance.getQudex().getCodeCollection()
				.getCodeArray()) {
			if (code.getId().equals(auxCode.getId()))
				return true;
		}
		return false;
	}

	/**
	 * @param id
	 * @param label
	 * @param memoText
	 * 
	 * @return XML String with qudex memo snippet, null if not valid XML
	 * @throws QudexException
	 */
	public static String generateMemoSnippet(String id, String label,
			String memoText, String language, List<String> documentRefs)
			throws QudexException {
		MemoType memoXml = MemoType.Factory.newInstance();

		if (id == null || id.equals("")) {
			throw new QudexException(
					"Memo snippet id null or empty. Invalid Qudex memo snippet...");
		}
		memoXml.setId(id);
		if (label != null && !label.equals(""))
			memoXml.setLabel(label);
		if (language != null && !language.equals(""))
			memoXml.setLanguage(language);
		if (memoText == null || memoText.equals("")) {
			throw new QudexException(
					"Memo text null or empty. Invalid Memo snippet to add to qudex instance...");
		}
		if (documentRefs.size() > 0) {
			for (String docRefStr : documentRefs) {
				MemoDocumentRef docRef = memoXml.addNewMemoDocumentRef();
				docRef.setId(QudexManagement.generateID());
				docRef.setSrc(docRefStr);
			}
		}
		MemoTextType memoTextXml = memoXml.addNewMemoText();

		memoTextXml.setId(generateID());
		memoTextXml.setStringValue(memoText);

		boolean isValidXml = memoXml.validate();
		if (isValidXml)
			return memoXml.toString();
		else
			return null;
	}

	/**
	 * 
	 * @param memoXml
	 * @return true if success, false otherwise
	 */
	public boolean addMemo(String memoXml) {
		MemoType memo;
		MemoCollectionType memoCollection = this.qudexInstance.getQudex()
				.getMemoCollection();
		try {
			if (memoCollection == null) {
				memoCollection = this.qudexInstance.getQudex()
						.addNewMemoCollection();
				memoCollection.setId(generateID());
			}
			MemoType auxMemo = MemoType.Factory.parse(memoXml);
			;
			if (isMemoPresent(auxMemo))
				return true;
			memo = memoCollection.addNewMemo();
			memo.setId(auxMemo.getId());
			if (auxMemo.getLabel() != null && !auxMemo.getLabel().equals(""))
				memo.setLabel(auxMemo.getLabel());
			if (auxMemo.getLanguage() != null
					&& !auxMemo.getLanguage().equals(""))
				memo.setLanguage(auxMemo.getLanguage());
			if (auxMemo.getMemoTextArray() != null
					&& auxMemo.getMemoTextArray().length > 0)
				memo.setMemoTextArray(auxMemo.getMemoTextArray());
			if (auxMemo.getMemoDocumentRefArray() != null
					&& auxMemo.getMemoDocumentRefArray().length > 0)
				memo.setMemoDocumentRefArray(auxMemo.getMemoDocumentRefArray());

			return true;
		} catch (XmlException e) {
			log.fatal(e.getMessage());
			return false;
		}
	}

	private boolean isMemoPresent(MemoType auxMemo) {
		for (MemoType memo : this.qudexInstance.getQudex().getMemoCollection()
				.getMemoArray()) {
			if (memo.getId().equals(auxMemo.getId()))
				return true;
		}
		return false;
	}

	// PUBLIC STATIC METHODS
	/**
	 * Generates a new ID to use in the QuDex elements Attr ID
	 * 
	 * @return the string representation of the UUID generated (QD********)
	 */
	public static String generateID() {
		UUID uuid = UUID.randomUUID();
		return ID_PREFIX + uuid.toString();
	}

	public boolean validateQudexInstance() {
		return this.qudexInstance.getQudex().validate();
	}

	/**
	 * Constructor
	 */
	public QudexManagement() {
		this.categoryManager = new CategoryManagement();
		this.codeManager = new CodeManagement();
		this.memoManager = new MemoManagement();
	}

	// RELATIONSHIPS MANAGEMENT
	/**
	 * Once all the Qudex XML bits have been generated, this method generates
	 * the objectRelation qudex elements and add them to the current Qudex
	 * Instance
	 */
	@SuppressWarnings("unchecked")
	public void createObjectsRelationships() {
		// AMG - (1) Generate Qudex Category relationships
		for (CategoryStruct cat : (List<CategoryStruct>) this.categoryManager
				.getElementsList()) {
			this.addQudexRelationShip(cat);
		}
		// AMG - (2) Generate Qudex Code relationships
		for (CodeStruct code : (List<CodeStruct>) this.codeManager
				.getElementsList()) {
			this.addQudexRelationShip(code);
		}
		// AMG - (3) Generate Qudex Memo relationships
		for (MemoStruct memo : (List<MemoStruct>) this.memoManager
				.getElementsList()) {
			this.addQudexRelationShip(memo);
		}
	}

	// METHODS
	/**
	 * @param element
	 *            either CategoryStruct, MemoStruct or CodeStruct instance
	 */
	public void addQudexRelationShip(Object element) {

		RelationCollectionType relCollection = this.getQudexInstance()
				.getQudex().getRelationCollection();
		if (relCollection == null) {
			relCollection = this.getQudexInstance().getQudex()
					.addNewRelationCollection();
			relCollection.setId(QudexManagement.generateID());
		}
		if (element instanceof CodeStruct) {
			CodeStruct codeS = (CodeStruct) element;
			for (String documentId : codeS.getDocumentRefsXmlIds()) {
				ObjectRelationType objectRel = relCollection
						.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (codeS.getCodeLanguage() != null
						&& !codeS.getCodeLanguage().equals(""))
					objectRel.setLanguage(codeS.getCodeLanguage());
				objectRel.setLabel("Code to Document relationship");

				objectRel.setObjectSource(codeS.getCodeXmlId());
				objectRel.setObjectTarget(documentId);
				objectRel.setObjectType(ObjectType.CODE_DOCUMENT);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATION_OF
								.name());
				// Document to Code equivalent relationship
				objectRel = relCollection.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (codeS.getCodeLanguage() != null
						&& !codeS.getCodeLanguage().equals(""))
					objectRel.setLanguage(codeS.getCodeLanguage());
				objectRel.setLabel("Document to Code relationship");

				objectRel.setObjectSource(documentId);
				objectRel.setObjectTarget(codeS.getCodeXmlId());
				objectRel.setObjectType(ObjectType.DOCUMENT_CODE);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATED_BY
								.name());
			}
		} else if (element instanceof MemoStruct) {
			MemoStruct memoS = (MemoStruct) element;
			for (String documentId : memoS.getDocumentRefsXmlIds()) {
				ObjectRelationType objectRel = relCollection
						.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (memoS.getMemoLanguage() != null
						&& !memoS.getMemoLanguage().equals(""))
					objectRel.setLanguage(memoS.getMemoLanguage());
				objectRel.setLabel("Memo to Document relationship");
				objectRel.setObjectSource(memoS.getMemoXmlId());
				objectRel.setObjectTarget(documentId);
				objectRel.setObjectType(ObjectType.MEMO_DOCUMENT);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATION_OF
								.name());
				// Document to Memo equivalent relationship
				objectRel = relCollection.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (memoS.getMemoLanguage() != null
						&& !memoS.getMemoLanguage().equals(""))
					objectRel.setLanguage(memoS.getMemoLanguage());
				objectRel.setLabel("Document to Memo relationship");
				objectRel.setObjectSource(documentId);
				objectRel.setObjectTarget(memoS.getMemoXmlId());
				objectRel.setObjectType(ObjectType.DOCUMENT_MEMO);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATED_BY
								.name());

			}
		} else if (element instanceof CategoryStruct) {
			CategoryStruct categoryS = (CategoryStruct) element;
			for (String documentId : categoryS.getDocumentRefsXmlIds()) {
				ObjectRelationType objectRel = relCollection
						.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (categoryS.getCategoryLanguage() != null
						&& !categoryS.getCategoryLanguage().equals(""))
					objectRel.setLanguage(categoryS.getCategoryLanguage());
				objectRel.setLabel("Category to Document relationship");
				objectRel.setObjectSource(categoryS.getCategoryXmlId());
				objectRel.setObjectTarget(documentId);
				objectRel.setObjectType(ObjectType.CATEGORY_DOCUMENT);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATION_OF
								.name());
				// Document to Category equivalent relationship
				objectRel = relCollection.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (categoryS.getCategoryLanguage() != null
						&& !categoryS.getCategoryLanguage().equals(""))
					objectRel.setLanguage(categoryS.getCategoryLanguage());
				objectRel.setLabel("Document to Category relationship");
				objectRel.setObjectSource(documentId);
				objectRel.setObjectTarget(categoryS.getCategoryXmlId());
				objectRel.setObjectType(ObjectType.DOCUMENT_CATEGORY);
				objectRel.setRelationName(RelationName.OTHER);
				objectRel
						.setOtherRelationName(Constants.OTHER_RELATIONSHIPS.IS_ANNOTATED_BY
								.name());

			}
			// Category Hierarchy
			for (String catParent : categoryS.getImmParents()) {
				ObjectRelationType objectRel = relCollection
						.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (categoryS.getCategoryLanguage() != null
						&& !categoryS.getCategoryLanguage().equals(""))
					objectRel.setLanguage(categoryS.getCategoryLanguage());
				objectRel.setLabel("Category to Category relationship");
				objectRel.setObjectSource(categoryS.getCategoryXmlId());
				String parentId = this.findParentCategoryId(catParent);
				if (parentId == null) {
					relCollection.removeObjectRelation(relCollection
							.sizeOfObjectRelationArray() - 1);
					continue;
				}
				objectRel.setObjectTarget(parentId);
				objectRel.setObjectType(ObjectType.CATEGORY_CATEGORY);
				// TODO Check whether this is the appropriate relationship
				objectRel.setRelationName(RelationName.IS_CHILD_OF);

				// Parent Category to child category relationship
				objectRel = relCollection.addNewObjectRelation();
				objectRel.setId(QudexManagement.generateID());
				if (categoryS.getCategoryLanguage() != null
						&& !categoryS.getCategoryLanguage().equals(""))
					objectRel.setLanguage(categoryS.getCategoryLanguage());
				objectRel.setLabel("Category to Category relationship");
				objectRel.setObjectSource(parentId);
				objectRel.setObjectTarget(categoryS.getCategoryXmlId());
				objectRel.setObjectType(ObjectType.CATEGORY_CATEGORY);
				objectRel.setRelationName(RelationName.IS_PARENT_OF);

			}
		}
	}

	@SuppressWarnings("unchecked")
	private String findParentCategoryId(String parentCatLabel) {
		for (CategoryStruct cat : (List<CategoryStruct>) this
				.getCategoryManager().getElementsList()) {
			if (cat.getCategoryDisplayLabel().equals(parentCatLabel)) {
				return cat.getCategoryXmlId();
			}
		}
		return null;
	}

	public void cleanManager() {
		if (this.getCategoryManager() != null)
			this.getCategoryManager().clearList();
		if (this.getCodeManager() != null)
			this.getCodeManager().clearList();
		if (this.getMemoManager() != null)
			this.getMemoManager().clearList();
		if (this.getQudexInstance() != null)
			this.getQudexInstance().setNil();
	}
}
